Skip to content

QVAC-27164 feat[api|bc]: support EmbeddingGemma 2 multimodal embeddings - #5464

Merged
gianni-cor merged 20 commits into
mainfrom
feat/sdk-embeddinggemma2
Nov 6, 2026
Merged

gianni-cor merged 20 commits into
mainfrom
feat/sdk-embeddinggemma2

Conversation

@maxim-smotrov

@maxim-smotrov maxim-smotrov commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

🎯 What problem does this PR solve?

SDK doesn't provide support for EmbeddingGemma 2 for multi-modal embeddings. The addon already added support in #5237.

📝 How does it solve it?

  • Add ordered inputs batches with text, file-path media, and base64 media while preserving the existing text calls.
  • Resolve the projector through the model lifecycle and expose context, media, and projector settings.
  • Update the wire contract, Python and Kotlin bindings, examples, documentation, and e2e catalog.
  • Keep this capability library-only. CLI OpenAI-compatible embeddings remain text-only.
  • Registry entries are tracked separately in QVAC-27164 feat[mod]: add EmbeddingGemma 2 to the registry #5463.

🧪 How was it tested?

  • Full local inference, SDK, and e2e build passed.
  • Inference lint/type checks and focused Bare tests passed, 13 tests and 67 assertions.
  • SDK lint/type checks, unit tests, and contract freshness checks passed.
  • Python generation check and Kotlin contract/JVM tests passed.
  • E2e formatting, type checks, and catalog checks passed.
  • Real-model e2e run on macOS.

💥 Breaking Changes

The generated Kotlin and Python EmbedRequest types now represent two alternatives. EmbedTextRequest requires text and forbids inputs. EmbedInputsRequest requires inputs and forbids text.

A single object with optional text and inputs would keep the old constructors, but its Zod refinement requiring exactly one field would not appear in the exported JSON Schema. Generated clients could then accept both fields or neither. With the union, Kotlin callers must select a variant with a required input field, and Python rejects both or neither during model validation, before RPC. The cost is the generated-client migration below. Existing QvacEmbeddings.embed convenience methods remain unchanged.

The TypeScript embed() overloads preserve precise result types: text: string returns one vector, while text: string[] and inputs return a batch of vectors. These overloads do not cause the generated-client migration; the request schema union does. Existing TypeScript text calls remain supported.

Kotlin

Direct construction of EmbedRequest now requires a request variant wrapper. The text value type is renamed from EmbedRequestText to EmbedTextRequestText.

BEFORE:

import io.tether.qvac.sdk.generated.schema.EmbedRequest
import io.tether.qvac.sdk.generated.schema.EmbedRequestText

val request = EmbedRequest(
    modelId = "m",
    text = EmbedRequestText.Variant1("hi"),
    requestId = "embed-1",
)

AFTER:

import io.tether.qvac.sdk.generated.schema.EmbedRequest
import io.tether.qvac.sdk.generated.schema.EmbedTextRequest
import io.tether.qvac.sdk.generated.schema.EmbedTextRequestText

val request = EmbedRequest.EmbedTextRequest(
    EmbedTextRequest(
        modelId = "m",
        text = EmbedTextRequestText.Variant1("hi"),
        requestId = "embed-1",
    ),
)

For text batches, use EmbedTextRequestText.Variant2(texts) inside the same wrapper. For multimodal batches, use EmbedRequest.EmbedInputsRequest wrapping an EmbedInputsRequest. When inspecting a request typed as EmbedRequest, select the variant and read its fields through .value.

The existing QvacEmbeddings.embed(modelId, text) and embed(modelId, texts) convenience methods remain supported and need no caller migration.

Python

The existing keyword constructor still works. EmbedRequest is now a Pydantic RootModel containing the selected request variant, so direct .model_id, .text, and .request_id access must move under .root.

BEFORE:

from tetherto.qvac_sdk import EmbedRequest

request = EmbedRequest(model_id="m", text="hi", request_id="embed-1")
model_id = request.model_id
text = request.text
request_id = request.request_id

AFTER:

from tetherto.qvac_sdk import EmbedRequest

request = EmbedRequest(model_id="m", text="hi", request_id="embed-1")
model_id = request.root.model_id
text = request.root.text
request_id = request.root.request_id

For the inputs variant, read request.root.inputs after selecting that variant. Continue passing the outer request to embed(transport, request); do not replace it with request.root. Wire serialization remains flat, with no root or value field added to the RPC payload.

🔌 API Changes

Load the model with modelConfig.projectionModelSrc, then pass text and media inputs:

const { embedding } = await embed({
  modelId,
  inputs: [
    'task: search result | query: an elephant',
    [{ media: { type: 'filePath', value: '/path/to/elephant.jpg' } }]
  ]
})

The result contains one vector per input.

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

License compliance — clean

No new dependency license findings in this PR.

Warn-only (shadow) mode — this check does not block merges yet.

Updated automatically by the canonical license compliance workflow.

NOTICE presence (advisory)

Missing NOTICE (advisory, does not block):

  • ./docs/website
  • ./packages/asr-ggml/benchmarks/server
  • ./packages/fabric/test/integration
  • ./packages/llm-llamacpp/benchmarks/performance
  • ./packages/llm-llamacpp/benchmarks/server
  • ./packages/inference-addon-cpp/mobile
  • ./packages/sdk-e2e
  • ./packages/embed-llamacpp/benchmarks/performance
  • ./packages/embed-llamacpp/benchmarks/server
  • ./packages/vla-ggml/sim/server
  • ./.github/actions/release-merge-guard
  • ./.github/bare-toolchain

@maxim-smotrov
maxim-smotrov marked this pull request as ready for review October 30, 2026 18:11
@maxim-smotrov
maxim-smotrov requested review from a team as code owners October 30, 2026 18:11
@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Review Status

Current Status: ❌ PENDING
Approvals so far: Member: 2

Pending reviews: Needs 1 Management or Team Lead.

@maxim-smotrov
maxim-smotrov force-pushed the feat/sdk-embeddinggemma2 branch from 539e642 to 9c4b45b Compare October 30, 2026 18:27
@greptile-apps

greptile-apps Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no new actionable issue was found.

Summary

Adds library-only text, image, and audio embeddings with EmbeddingGemma 2 while preserving existing text calls.

  • Embedding calls can now turn ordered text and media into vectors.
  • SDK clients can choose text or media batches with typed requests.
  • EmbeddingGemma 2 now has cross-platform checks for media vectors.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Load embedding model] --> B[Resolve model and optional projector]
  B --> C[Create and load addon]
  D[Embed text or ordered inputs] --> E[Admit one request per model]
  E --> F[Check projector before reading media]
  F --> G[Prepare text and media bytes]
  C --> H[Run addon]
  G --> H
  H --> I[Normalize vectors and return results]
  I --> J[Finish cancellation cleanup]
  J --> K[Release request slot]
Loading

Reviews (21) · Last reviewed commit: "Merge branch 'main' into feat/sdk-embedd..." · Reviewed by Greptile

@maxim-smotrov maxim-smotrov changed the title QVAC-27164 feat[api]: support EmbeddingGemma 2 multimodal embeddings QVAC-27164 feat[api|bc]: support EmbeddingGemma 2 multimodal embeddings Nov 2, 2026
@maxim-smotrov
maxim-smotrov force-pushed the feat/sdk-embeddinggemma2 branch 2 times, most recently from cb9fb86 to c367ca9 Compare November 3, 2026 14:19
@maxim-smotrov maxim-smotrov added the test-e2e-smoke Triggers smoke e2e test suite [Currently SDK-only] label Nov 3, 2026
@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

QVAC E2E — base run recorded

Base: run 41661064462 · suite smoke · sha c367ca9

Platform Passed Total Failed
android/android 187 213 0
desktop/linux 213 213 0
desktop/macos 210 213 0
desktop/windows 213 213 0
ios/ios 183 213 0

All recorded platforms are green — nothing for test-e2e-rerun-failed to re-run.

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — tests changed by this PR

This smoke run additionally covers 89 test(s) that are not tagged smoke, adding roughly 59.1 min.

Catalog: 624 tests · smoke: 124 · touched by this PR: 129 (40 already in smoke)

Changed file Matched via Tests
packages/inference/src/handlers/assess-model-fit sdk handler 26
packages/sdk/src/client/api/embed.ts sdk api 85
catalog/embeddinggemma2-tests.ts definitions (whole file) 4
catalog/platform-skips.ts import graph 18
catalog/test-definitions.ts definitions (whole file) 18
runners/js/shared/executors/embeddinggemma2-executor.ts executor pattern 4
89 added test id(s)
model-load-llm-load-mode-none
model-load-llm-legacy-no-mmap-rejected
model-load-llm-prompt-cache-keys
model-load-llm-cache-checkpoints-out-of-range-rejected
model-load-embedding-rank-pooling-rejected
model-reload-llm
embed-long-text
embed-empty-text
embed-similarity
embed-unicode
embed-very-short
embed-code-snippet
embed-multilingual
embed-special-chars
embed-numbers-only
embed-semantic-similarity
embeddinggemma2-image-retrieval
embeddinggemma2-audio-deterministic
embeddinggemma2-text-parity
embeddinggemma2-reject-corrupt-media
vector-index-remove-contains
vector-index-write-load
sharded-model-hash-validation
sharded-model-backward-compatibility
sharded-model-progress
sharded-model-resume
sharded-model-cancellation
sharded-model-batch-inference
sharded-model-long-text-inference
sharded-model-llm-completion
sharded-model-llm-reload
sharded-model-llm-missing-shards
http-sharded-embed-progress
http-archive-embed-load
http-archive-embed-progress
http-archive-embed-inference
model-fit-probe-llm
model-fit-probe-embeddings
model-fit-probe-parakeet
model-fit-probe-whisper
model-fit-probe-bci
model-fit-probe-tts-chatterbox
model-fit-probe-audiogen
model-fit-probe-diffusion
model-fit-probe-diffusion-esrgan
model-fit-probe-upscaler
model-fit-probe-ocr
model-fit-probe-translation
error-invalid-response-type
error-model-load-failed
… and 39 more

These changed files map to no test, so nothing was added for them — review by hand if they affect behaviour:

  • packages/inference/src/plugins/builtin/llamacpp-embedding
  • catalog/resource-table.ts
  • catalog/resources/resource-table.json
  • catalog/resources/skip-matrix.json
  • runners/js/desktop/consumer.ts
  • runners/js/electron/consumer.ts
  • runners/js/mobile/consumer.ts

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

QVAC E2E — windows — ✅ all tests passed (213/213, 427s)

Config: suite=smoke · filter=(none) · exclude=(none)
Inference: branch:c367ca9660995045b895d40bc70813a584cf29e2:@qvac/inference@0.23.0 · Test-suite: branch:c367ca9660995045b895d40bc70813a584cf29e2:@qvac/test-suite@0.13.0
View run · Artifacts: reports

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

QVAC E2E — linux — ✅ all tests passed (213/213, 399s)

Config: suite=smoke · filter=(none) · exclude=(none)
Inference: branch:c367ca9660995045b895d40bc70813a584cf29e2:@qvac/inference@0.23.0 · Test-suite: branch:c367ca9660995045b895d40bc70813a584cf29e2:@qvac/test-suite@0.13.0
View run · Artifacts: reports

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

QVAC E2E — macos — ✅ all tests passed (210/213, 437s)

Config: suite=smoke · filter=(none) · exclude=(none)
Inference: branch:c367ca9660995045b895d40bc70813a584cf29e2:@qvac/inference@0.23.0 · Test-suite: branch:c367ca9660995045b895d40bc70813a584cf29e2:@qvac/test-suite@0.13.0
View run · Artifacts: reports

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

QVAC E2E — ios — ✅ all tests passed (183/213, 1192s)

Config: suite=smoke · filter=(none) · exclude=(none)
Inference: branch:c367ca9660995045b895d40bc70813a584cf29e2:@qvac/inference@0.23.0 · Test-suite: branch:c367ca9660995045b895d40bc70813a584cf29e2:@qvac/test-suite@0.13.0
Device pool: iPhone 17 Pro - iOS 26 — ☀️ highly available · Billing: UNMETERED
View run · Artifacts: reports · Device Farm logs

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

QVAC E2E — android — ✅ all tests passed (187/213, 3942s)

Config: suite=smoke · filter=(none) · exclude=(none)
Inference: branch:c367ca9660995045b895d40bc70813a584cf29e2:@qvac/inference@0.23.0 · Test-suite: branch:c367ca9660995045b895d40bc70813a584cf29e2:@qvac/test-suite@0.13.0
Device pool: Google Pixel 9 Pro - Android 15 — ☀️ highly available · Billing: UNMETERED
View run · Artifacts: reports · Device Farm logs

@maxim-smotrov
maxim-smotrov force-pushed the feat/sdk-embeddinggemma2 branch from 2c66681 to e9a8bc4 Compare November 4, 2026 15:17
dev-nid
dev-nid previously approved these changes Nov 5, 2026
Comment thread packages/sdk/test/embed-client.test.ts Resolved

@lauripiisang lauripiisang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way we do e2e is a bit different now so the implementation should be changed to match there, plus the schemas could use titles so they get better naming in generated code.

Comment thread packages/inference/src/schemas/embed.ts Resolved
Comment thread packages/inference/src/schemas/embed.ts Resolved
Signed-off-by: Maksim Smatrou <maxim.smotrov@itrexgroup.com>
Signed-off-by: Maksim Smatrou <maxim.smotrov@itrexgroup.com>
Signed-off-by: Maksim Smatrou <maxim.smotrov@itrexgroup.com>
Signed-off-by: Maksim Smatrou <maxim.smotrov@itrexgroup.com>
Signed-off-by: Maksim Smatrou <maxim.smotrov@itrexgroup.com>
Signed-off-by: Maksim Smatrou <maxim.smotrov@itrexgroup.com>
Signed-off-by: Maksim Smatrou <maxim.smotrov@itrexgroup.com>
Comment thread packages/sdk-python/tests/test_embed.py Resolved
Signed-off-by: Maksim Smatrou <maxim.smotrov@itrexgroup.com>
@gianni-cor
gianni-cor merged commit 54d496a into main Nov 6, 2026
51 checks passed
@gianni-cor
gianni-cor deleted the feat/sdk-embeddinggemma2 branch November 6, 2026 09:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test-e2e-smoke Triggers smoke e2e test suite [Currently SDK-only]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants